无监督学习相比监督学习,它的学习算法只有输入数据,并没有对应的标签,所以对于训练出的结果需要研究者自行进行定义,而评估无监督算法结果的唯一办法就是人工检查。
无监督算法通常用于探索性的目的,它可以帮助研究者更好地理解数据,同时它也可以作为监督算法的预处理。
预处理与缩放:
StandardScaler:确保每个特征的平均值为0、方差为1,使所有特征都位于同一量级。
RobustScaler: 当数据中存在许多异常值导致均值和方差异常时使用,它使用四分位数对数据进行处理。
MinMaxScaler: 离差标准化,是对原始数据的线性变换,但是它存在当新数据进入时需要重新计算的缺点。
Normalizer: 可以选用L1、L2或者max对数据进行正则化。
主成分分析(PCA):
PCA(Principal Component Analysis),即主成分分析方法,是一种使用最广泛的数据降维算法。PCA的主要思想是将n维特征映射到k维上,这k维是全新的正交特征也被称为主成分,是在原有n维特征的基础上重新构造出来的k维特征。PCA的工作就是从原始的空间中顺序地找一组相互正交的坐标轴,新的坐标轴的选择与数据本身是密切相关的。其中,第一个新坐标轴选择是原始数据中方差最大的方向,第二个新坐标轴选取是与第一个坐标轴正交的平面中使得方差最大的,第三个轴是与第1,2个轴正交的平面中方差最大的。依次类推,可以得到n个这样的坐标轴。通过这种方式获得的新的坐标轴,我们发现,大部分方差都包含在前面k个坐标轴中,后面的坐标轴所含的方差几乎为0。于是,我们可以忽略余下的坐标轴,只保留前面k个含有绝大部分方差的坐标轴。事实上,这相当于只保留包含绝大部分方差的维度特征,而忽略包含方差几乎为0的特征维度,实现对数据特征的降维处理。
以上引用自Microstrong的博客
一个直观的例子:我们以每一列为一条记录,每一行为一个字段,给出原始数据
经过中心化处理(每个字段的值减去字段均值)后,数据变化为
其可视化为
如果说我们需要将二维数据降为一维数据,则需要找到一条直线进行投影,用这个投影代替原始数据,所以选择这个直线(或者超平面)成了一个问题。选择的原则是希望投影后的投影尽可能分散,比如说如果以X轴为新的方向的话,则最左边的两个点就重合,这样(-1,-2)这个点的信息就丢失了,所以可以找通过第一、三象限的斜线投影,而这些点投影上去后的区分度还是很好,也就是方差比较大。
以上引用自张洋的博客
NMF(Non-negative matrix factorization,非负矩阵分解):
NMF和SVD的都是矩阵分解,但是目标不同,NMF希望将矩阵分解成两个子矩阵
而这两个矩阵都是非负,且可以从原矩阵中提取权重和特征两个不同的矩阵出来,其中V的列向量是W矩阵中所有列向量的加权和,而权重系数就是H对应的列向量的元素,也就是系数矩阵。之后就能用系数矩阵代替原始矩阵,从而实现对原始矩阵的降维。
t-SNE算法:
先从SNE说起,它的基本思想是在高维空间中相似的数据点,映射到低维空间后也应该是距离相似的。只不过传统的距离计算方法使用欧式距离进行计算,但是SNE采用的是将距离关系转化为用条件概率进行相似性表示,也就是考虑高维空间中的Xi和Xj,Xi以条件概率Pj|i选择Xj作为它的邻近点。考虑以Xi为中心点的高斯分布,若Xj越靠近Xi,则Pj|i越大。反之,若两者相距较远,则Pj|i极小。所以Pj|i的定义如下:
将其映射到低维空间后得到Xi和Xj在低维上的映射Yi和Yj的距离,其条件概率为:
如果要满足降维之后仍然能真实反映在高维上的数据分布,这样就要满足Pj|i=Qj|i,所以要通过一个代价函数来测量两个分布之间的相似度,这个算法就是经典的KL距离(Kullback-Leibler Divergence),SNE期望最小化代价函数:
看起来只要对这个函数进行最小化求解就行,但是这个函数存在一个问题。比如在高维空间中两个数据点较远,也就是Pj|i较小,但是在低维空间中却出现了两者较近,也就是Qj|i较大,这样的话上述函数就会整体偏小,但这明显不符合进一步优化的思路,我们需要使得出现不一致的情况下的惩罚较大才能使得梯度快速下降,所以综合来看,SNE代价函数更关注局部结构,而忽略了全局结构。
所以为解决这个问题,首先引入了对称SNE,这个方法对离群数据进行了对称化处理,它将距离定义为:
其中n为所有点的数量。
变化之后的代价函数更换为:
不过对称SNE的实际效果只是略好于SNE,没有从根本上解决问题。
所以t-SNE是什么呢?
这里的t指的是t分布,因为在出现异常点的情况下,它对异常点不敏感,不会像高斯分布一样尽可能拟合各个点而导致对大部分点的拟合程度不佳,使用t分布能够很好的对异常点进行排除,其效果好于高斯分布。此时,Qij变化为:
总结:
t-SNE算法主要在SNE上做了两个改进:
- 将SNE改成对称SNE
- 在低维空间中采用t分布代替高斯分布,而高维空间不变。
代码实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.datasets import fetch_lfw_people
from sklearn.neighbors import KNeighborsClassifier
from sklearn.decomposition import NMF
from sklearn.datasets import load_digits
from sklearn.manifold import TSNE
import mglearn
import numpy as np
cancer = load_breast_cancer()
digits = load_digits()
if __name__ == "__main__":
#利用PCA将cancer数据集可视化
'''
fig,axes = plt.subplots(15,2,figsize=(10,20))
malignant = cancer.data[cancer.target==0]
benign = cancer.data[cancer.target==1]
ax = axes.ravel()
for i in range(30):
_,bins = np.histogram(cancer.data[:,i],bins=50)
ax[i].hist(malignant[:,i],bins=bins,color=mglearn.cm3(0),alpha=.5)
ax[i].hist(benign[:,i],bins=bins,color=mglearn.cm3(2),alpha=.5)
ax[i].set_title(cancer.feature_names[i])
ax[i].set_yticks(())
ax[0].set_xlabel("Feature magnitude")
ax[0].set_ylabel("Frequency")
ax[0].legend(["malignant","benign"],loc="best")
fig.tight_layout()
plt.show()
'''
cancer = load_breast_cancer()
scaler = StandardScaler()
scaler.fit(cancer.data)
X_scaled = scaler.transform(cancer.data)
#保留数据的前两个主要成分
pca = PCA(n_components=2)
pca.fit(X_scaled)
#对乳腺癌数据拟合PCA模型
pca.fit(X_scaled)
#将数据变换到前两个主成分的方向上
X_pca = pca.transform(X_scaled)
print("Original shape:{}".format(str(X_scaled.shape)))
print("Reduced shape:{}".format(str(X_pca.shape)))
'''
plt.figure(figsize=(8,8))
mglearn.discrete_scatter(X_pca[:,0],X_pca[:,1],cancer.target)
plt.legend(cancer.target_names,loc="best")
plt.gca().set_aspect("equal")
plt.xlabel("First principal component")
plt.ylabel("Second principal component")
plt.show()
'''
print("PCA component shape:{}".format(pca.components_.shape))
print("PCA components:\n{}".format(pca.components_))
#用热图将系数可视化
'''
plt.matshow(pca.components_,cmap='viridis')
plt.yticks([0,1],["First component","Second component"])
plt.colorbar()
plt.xticks(range(len(cancer.feature_names)),cancer.feature_names,rotation=60,ha="left")
plt.ylabel("Principal components")
plt.show()
'''
#特征提取的特征脸
people = fetch_lfw_people(min_faces_per_person=20,resize=0.7)
image_shape=people.images[0].shape
'''
fix,axes=plt.subplots(2,5,figsize=(15,8),subplot_kw={'xticks':(),'yticks':()})
for target,image,ax in zip(people.target,people.images,axes.ravel()):
ax.imshow(image)
ax.set_title(people.target_names[target])
print("people.images.shape:{}".format(people.images.shape))
print("Number of classes:{}".format(len(people.target_names)))
plt.show()
'''
#计算每个目标出现的次数
counts = np.bincount(people.target)
#将次数和目标名称一起打印出来
for i,(count,name) in enumerate(zip(counts,people.target_names)):
print("{0:25} {1:3}".format(name,count),end=' ')
if(i+1)%3 ==0:
print()
mask = np.zeros(people.target.shape,dtype=np.bool)
for target in np.unique(people.target):
mask[np.where(people.target==target)[0][:50]]=1
X_people=people.data[mask]
y_people=people.target[mask]
#将灰度值缩放到0到1之间,而不是在0到255之间
#以得到更好的数据稳定性
X_people = X_people/255
#将数据分为训练集和测试集
X_train,X_test,y_train,y_test = train_test_split(X_people,y_people,stratify=y_people,random_state=0)
#使用一个邻居构建KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train,y_train)
print("\n")
print("Test set score of 1-nn:{:.2f}".format(knn.score(X_test,y_test)))
'''
mglearn.plots.plot_pca_whitening()
plt.show()
'''
pca = PCA(n_components=100,whiten=True,random_state=0).fit(X_train)
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)
print("X_train_pca.shape:{}".format(X_train_pca.shape))
knn = KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train_pca,y_train)
print("Test set accuracy:{:.2f}".format(knn.score(X_test_pca,y_test)))
print("pca.components_.shape:{}".format(pca.components_.shape))
'''
fix,axes = plt.subplots(3,5,figsize=(15,12),subplot_kw={'xticks':(),'yticks':()})
for i,(component,ax) in enumerate(zip(pca.components_,axes.ravel())):
ax.imshow(component.reshape(image_shape),cmap="viridis")
ax.set_title("{}.component".format((i+1)))
plt.show()
'''
'''
mglearn.plots.plot_pca_faces(X_train,X_test,image_shape)
plt.show()
'''
'''
mglearn.discrete_scatter(X_train_pca[:,0],X_train_pca[:,1],y_train)
plt.xlabel("First principal component")
plt.ylabel("Second principal component")
plt.show()
'''
'''
mglearn.plots.plot_nmf_illustration()
plt.show()
'''
'''
mglearn.plots.plot_nmf_faces(X_train,X_test,image_shape)
plt.show()
'''
nmf = NMF(n_components=15,random_state=0)
nmf.fit(X_train)
X_train_nmf = nmf.transform(X_train)
X_test_nmf = nmf.transform(X_test)
'''
fix,axes=plt.subplots(3,5,figsize=(15,12),subplot_kw={'xticks':(),'yticks':()})
for i,(component,ax) in enumerate(zip(nmf.components_,axes.ravel())):
ax.imshow(component.reshape(image_shape))
ax.set_title("{}.component".format(i))
plt.show()
'''
compn = 3
#按照第3个分量排序,绘制前10张图像
inds = np.argsort(X_train_nmf[:,compn])[::-1]
'''
fig,axes = plt.subplots(2,5,figsize=(15,8),subplot_kw={'xticks':(),'yticks':()})
for i,(ind,ax) in enumerate(zip(inds,axes.ravel())):
ax.imshow(X_train[ind].reshape(image_shape))
'''
compn = 7
#按照第7个分量排序,绘制前10张图像
inds = np.argsort(X_train_nmf[:,compn])[::-1]
'''
fig,axes = plt.subplots(2,5,figsize=(15,8),subplot_kw={'xticks':(),'yticks':()})
for i,(ind,ax) in enumerate(zip(inds,axes.ravel())):
ax.imshow(X_train[ind].reshape(image_shape))
'''
S = mglearn.datasets.make_signals()
'''
plt.figure(figsize=(6,1))
plt.plot(S,'-')
plt.xlabel("Time")
plt.ylabel("Signal")
plt.show()
'''
#将数据混合成100维的状态
A = np.random.RandomState(0).uniform(size=(100,3))
X = np.dot(S,A.T)
print("Shape of measurements:{}".format(X.shape))
#用NMF还原三个信号
nmf = NMF(n_components=3,random_state=42)
S_ = nmf.fit_transform(X)
print("Recovered signal shape:{}".format(S_.shape))
#用PCA还原三个信号
pca = PCA(n_components=3)
H = pca.fit_transform(X)
'''
models=[X,S,S_,H]
names = ['Observations(first three measurements)','True sources','NMF recovered signals','PCA recovered signals']
fig,axes=plt.subplots(4,figsize=(8,4),gridspec_kw={'hspace':.5},subplot_kw={'xticks':(),'yticks':()})
for model,name,ax in zip(models,names,axes):
ax.set_title(name)
ax.plot(model[:,:3],'-')
plt.show()
'''
'''
fig,axes=plt.subplots(2,5,figsize=(10,5),subplot_kw={'xticks':(),'yticks':()})
for ax,img in zip(axes.ravel(),digits.images):
ax.imshow(img)
plt.show()
'''
#构建一个PCA模型
pca = PCA(n_components=2)
pca.fit(digits.data)
#将digits数据变换到前两个主成分的方向上
digits_pca = pca.transform(digits.data)
colors=['#476A2A','#7851B8','#BD3430','#4A2D4E','#875525','#A83683','#4E655E','#853541','#3A3120','#535D8E']
'''
plt.figure(figsize=(10,10))
plt.xlim(digits_pca[:,0].min(),digits_pca[:,0].max())
plt.ylim(digits_pca[:,1].min(),digits_pca[:,1].max())
for i in range(len(digits.data)):
#将数据实际绘制成文本,而不是散点
plt.text(digits_pca[i,0],digits_pca[i,1],str(digits.target[i]),color=colors[digits.target[i]],fontdict={'weight':'bold','size':9})
plt.xlabel("First principal component")
plt.ylabel("Second principal component")
plt.show()
'''
tsne = TSNE(random_state=42)
#使用fit_transform而不是fit,因为TSNE没有transform方法
digits_tsne = tsne.fit_transform(digits.data)
'''
plt.figure(figsize=(10,10))
plt.xlim(digits_tsne[:,0].min(),digits_tsne[:,0].max()+1)
plt.ylim(digits_tsne[:,1].min(),digits_tsne[:,1].max()+1)
for i in range(len(digits.data)):
#将数据实际绘制成文本,而不是散点
plt.text(digits_tsne[i,0],digits_tsne[i,1],str(digits.target[i]),
color=colors[digits.target[i]],
fontdict={'weight':'bold','size':9})
plt.xlabel('t-SNE feature 0')
plt.xlabel('t-SNE feature 1')
plt.show()
'''